/*
 * tsk_enc.c
 *
 *  Created on: 06.05.2011
 *      Author: Millbrook Instrument
 */


#include <string.h>
#include <stdio.h>
#include <stdint.h>

#include <stm32f10x.h>

#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include "platform_config.h"

#include "tsk_enc.h"

// Идентификатор сообщений от интерфейса управления
xQueueHandle xEncMessageQueue = NULL;

/*
 * Текущее и прошлое состояния кнопки
 * Bit_SET - отпущена
 * Bit_RESET - нажата
 */
uint8_t SW_State = Bit_SET;
uint8_t SW_State_Old = Bit_SET;

/*
 * Переменные состояния энкодера
 */
uint8_t 	EncState;
int32_t 	EncData = 0, EncOldData = 0;

void vStartEncoder( unsigned portBASE_TYPE uxPriority )
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;

	// Выводы энкодера притянуты к VCC
	GPIO_InitStructure.GPIO_Pin	= ENC_A_PIN | ENC_B_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init( ENC_PORT, &GPIO_InitStructure );

	// Кнопка внутренне притянута к VCC
	GPIO_InitStructure.GPIO_Pin = ENC_SW_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init( ENC_PORT, &GPIO_InitStructure );
	GPIO_EXTILineConfig( GPIO_PortSourceGPIOC, GPIO_PinSource13 );

	// Прерывание EXTI по обоим фронтам
	EXTI_InitStructure.EXTI_Line = EXTI_Line13;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init( &EXTI_InitStructure );

	/*
	 * Новое распределение таймеров.
	 * Ревизия от 4 июня 2011 года.
	 * 1 Базовый таймер (TIM7) обслуживают кнопку.
	 * TIM7 - антидребезг (50 мс автоматическая перезагрузка).
	 * TIM6 - энкодер, частота обновления 10 кГц.
	 */

	RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM6 | RCC_APB1Periph_TIM7, ENABLE );
	DBGMCU_Config( DBGMCU_TIM6_STOP | DBGMCU_TIM7_STOP, ENABLE );

	TIM_PrescalerConfig( TIM6, 35, TIM_PSCReloadMode_Update ); // 1 МГц
	TIM_SetAutoreload( TIM6, 99 ); // 10 кГц
	TIM_ITConfig( TIM6, TIM_IT_Update, ENABLE );

	TIM_PrescalerConfig( TIM7, 3599, TIM_PSCReloadMode_Update); // 10 кГц
	TIM_SetAutoreload( TIM7, 499 ); // 50 ms
	TIM_ITConfig( TIM7, TIM_IT_Update, ENABLE );

	NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init( &NVIC_InitStructure );

	NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
	NVIC_Init( &NVIC_InitStructure );

	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
	NVIC_Init( &NVIC_InitStructure );

	xEncMessageQueue = xQueueCreate( 2, sizeof(  xEncMessage ) );
	TIM_Cmd( TIM6, ENABLE );
	TIM_Cmd( TIM7, ENABLE );
}

///*******************************************************************************
//* Function Name  : EXTI15_10_IRQHandler
//* Description    : This function handles External lines 15 to 10 interrupt request.
//* Input          : None
//* Output         : None
//* Return         : None
//*******************************************************************************/
//void EXTI15_10_IRQHandler(void)
//{
//	if( EXTI_GetITStatus( EXTI_Line13 ) )
//	{
//		// Сбросим таймер по изменению состояния кнопки
//		TIM_SetCounter( TIM7, 0 );
//		EXTI_ClearITPendingBit( EXTI_Line13 );
//	}
//}

/*******************************************************************************
* Function Name  : TIM7_IRQHandler
* Description    : This function handles TIM7 global interrupt request.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void TIM7_IRQHandler(void)
{
static int long_click = 0;
xEncMessage xMessage;
portBASE_TYPE xHigherPriorityTaskWoken = pdTRUE;

	if( TIM_GetITStatus( TIM7, TIM_IT_Update ) )
	{
		TIM_ClearITPendingBit( TIM7, TIM_IT_Update );

		SW_State = GPIO_ReadInputDataBit( ENC_PORT, ENC_SW_PIN );
		if( SW_State != SW_State_Old )
		{
			SW_State_Old = SW_State;
			if( SW_State == Bit_SET )
			{
				if( long_click >= 1800 )
					xMessage.xEvent = EEvent_LongClick;
				else
					xMessage.xEvent = EEvent_Click;
				long_click = 0;

				xMessage.iData = 0;
				xQueueSendFromISR( xEncMessageQueue, ( void * ) &xMessage, &xHigherPriorityTaskWoken );
				if( xHigherPriorityTaskWoken )
					taskYIELD();
			}
		}

		if( SW_State == Bit_RESET )
			long_click += 50;
	}
}

/**
 * @brief Передать сообщение о вращении по часовой стрелке
 */
static void vEncCW()
{
xEncMessage xMessage;
portBASE_TYPE xHigherPriorityTaskWoken = pdTRUE;

	xMessage.xEvent = EEvent_CW;
	xMessage.iData = EncData / 4 ;
	xQueueSendFromISR( xEncMessageQueue, ( void * ) &xMessage, &xHigherPriorityTaskWoken );
	if( xHigherPriorityTaskWoken )
		taskYIELD();
}

/**
 * @brief Передать сообщение о вращении против часовой стрелки
 */
static void vEncCCW()
{
xEncMessage xMessage;
portBASE_TYPE xHigherPriorityTaskWoken = pdTRUE;

	xMessage.xEvent = EEvent_CCW;
	xMessage.iData = EncData / 4 ;
	xQueueSendFromISR( xEncMessageQueue, ( void * ) &xMessage, &xHigherPriorityTaskWoken );
	if( xHigherPriorityTaskWoken )
		taskYIELD();
}

/*******************************************************************************
* Function Name  : TIM6_IRQHandler
* Description    : This function handles TIM6 global interrupt request.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void TIM6_IRQHandler(void)
{

	if( TIM_GetITStatus( TIM6, TIM_IT_Update ) )
	{
		TIM_ClearITPendingBit( TIM6, TIM_IT_Update );

		uint32_t input =	ENC_PORT->IDR;
		uint8_t new;

		new = ( input & ( ENC_A_PIN | ENC_B_PIN ) ) >> 8;
		switch( EncState )
		{
			case 2:
				if( new == 3 )
					EncData--;
				if( new == 0 )
					EncData++;
				break;

			case 0:
				if( new == 2 )
					EncData--;
				if( new == 1 )
					EncData++;
				break;

			case 1:
				if( new == 0 )
					EncData--;
				if( new == 3 )
					EncData++;
				break;

			case 3:
				if( new == 1 )
					EncData--;
				if( new == 2 )
					EncData++;
				break;
		}
		EncState = new;

		switch( EncData - EncOldData )
		{
			case 4: // CW
				if( xEncMessageQueue )
					vEncCW();
				EncOldData = EncData;
				break;

			case -4:
				if( xEncMessageQueue )
					vEncCCW();
				EncOldData = EncData;
				break;
		}
	}
}
